home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 12 - 1996 / 12.11 Nov 96 / DebuggingStarter Code / Debug.c next >
Encoding:
C/C++ Source or Header  |  1996-09-09  |  14.9 KB  |  633 lines  |  [TEXT/CWIE]

  1. /*________________________________________________________________________________
  2.     Debug.c: general purpose debugging code.
  3. ________________________________________________________________________________*/
  4.  
  5. #include "Debug.h"
  6.  
  7.  
  8. #if DEBUG    // [
  9.  
  10. // avoid recursive calls with DebugTraps
  11. #include "DebugTrapsOff.h"
  12.  
  13.  
  14. static void        AppendVariableName( const char *cVarName, StringPtr msg );
  15. static void        AppendUserMsg( ConstStr255Param userMsg, StringPtr msg );
  16. static Boolean    DebugGetErrorString( OSErr err, StringPtr errString);
  17.     
  18.  
  19. #if PRAGMA_MARK_SUPPORTED
  20. #pragma mark --- Utilities ---
  21. #endif
  22.  
  23. /*________________________________________________________________________________
  24.     Convert a number to a string.
  25.     
  26.     This version doesn't move memory, unlike the system version of NumToString,
  27.     so it can be used safely anywhere.
  28. ________________________________________________________________________________*/
  29.     static void
  30. NumToStringBase(
  31.     long        num, 
  32.     StringPtr    outString, 
  33.     UInt16        base,
  34.     Boolean        isSigned)
  35.     {
  36.     Assert( base >= 2 && base <= 16, "\pNumToStringBase: illegal base");
  37.     Assert( ( ! isSigned ) || base == 10, "\pNumToStringBase: signed only applies to base 10");
  38.  
  39.     if (num == 0)
  40.         {
  41.         outString[ 0 ] = 1;
  42.         outString[ 1 ] = '0';
  43.         }
  44.     else
  45.         {
  46.         Boolean        isNegative = false;
  47.         UInt32        ulNum;
  48.         UInt8        digitIndex;
  49.         UInt8        tempBuffer[ 64 ];    // not a pascal string
  50.         UInt8        *outPtr;
  51.  
  52.         // only base 10 is treated as signed. If so, convert using positive
  53.         // number and remember that it's negative for later.
  54.         if (num < 0 &&
  55.             base == 10 &&
  56.             isSigned)
  57.             {
  58.             isNegative = true;
  59.             num        = -num;
  60.             }
  61.         
  62.         ulNum = (UInt32) num;
  63.     
  64.         // add digits in convenient (reverse) order
  65.         outPtr    = &tempBuffer[ 0 ];
  66.         while ( ulNum > 0 )
  67.             {
  68.             UInt32    temp;
  69.             UInt8    digitVal;
  70.             static unsigned char    sDigits[] = "0123456789ABCDEF";
  71.     
  72.             temp        = ulNum/base;
  73.             digitVal    = ulNum - (temp * base);
  74.     
  75.             *outPtr++    = sDigits[ digitVal ];
  76.     
  77.             ulNum = temp;
  78.             }
  79.     
  80.         if ( isNegative )
  81.             {
  82.             *outPtr++ = '-';
  83.             }
  84.     
  85.         // reverse the digits so they're in the correct order
  86.         outString[0] = outPtr - &tempBuffer[ 0 ];
  87.     
  88.         for (digitIndex = 1; digitIndex <= outString[0]; ++digitIndex)
  89.             {
  90.             outString[ digitIndex ] = *--outPtr;
  91.             }
  92.         }
  93.     }
  94.  
  95.  
  96. /*________________________________________________________________________________
  97.     Convert a number to a string (same as NumToString).
  98.     
  99.     Unlike the system version, does not move memory.
  100. ________________________________________________________________________________*/
  101.     void
  102. DebugNumToString(
  103.     long        num,
  104.     StringPtr    string)
  105.     {
  106.     NumToStringBase( num, string, 10, TRUE);
  107.     }
  108.  
  109.  
  110.     static void
  111. CopyPString(
  112.     ConstStr255Param    srcStr,
  113.     StringPtr            destStr)
  114.     {
  115.     BlockMoveData( srcStr, destStr, 1UL + srcStr[ 0 ] );
  116.     }
  117.  
  118.                         
  119.     static void
  120. AppendPString(
  121.     ConstStr255Param    appendStr,
  122.     StringPtr            destStr)
  123.     {
  124.     UInt8    lengthToAppend;
  125. #define kMaxStringLength    (UInt16)255
  126.  
  127.     // if the new length would exceed the maximum string length, then
  128.     // just append as much as possible
  129.     lengthToAppend    = appendStr[0];
  130.     if ( lengthToAppend + (UInt32)destStr[0] > kMaxStringLength )
  131.         lengthToAppend    = kMaxStringLength - destStr[0];
  132.  
  133.     BlockMoveData( &appendStr[1], &destStr[destStr[0]+1], lengthToAppend);
  134.     destStr[0]    += lengthToAppend;
  135.     }
  136.  
  137.  
  138.     
  139.     static void
  140. CToPString(
  141.     const char    *cString,
  142.     StringPtr    pString)
  143.     {
  144.     UInt32        length;
  145.     UInt8        *curOut;
  146.     
  147.     length        = 0;
  148.     curOut        = &pString[1];
  149.     #define kMaxPascalStringLength    255
  150.     while ( length < kMaxPascalStringLength )
  151.         {
  152.         if ( (curOut[length] = cString[length]) == 0)
  153.             break;
  154.         ++length;
  155.         }
  156.     
  157.     pString[0]    = length;
  158.     }
  159.  
  160.  
  161.  
  162.  
  163. /*________________________________________________________________________________
  164.     Example:
  165.         GetDebugNumString( "\pThe value is", 229, result )
  166.         
  167.     returns the following string in 'result':
  168.         "\pThe value is: 229"
  169. ________________________________________________________________________________*/    
  170.     void
  171. GetDebugNumString(
  172.     ConstStr255Param     failStr,
  173.     long                num,
  174.     StringPtr            msg)
  175.     {
  176.     Str32    numStr;
  177.     
  178.     CopyPString( failStr, msg);
  179.     AppendPString( "\p: ", msg);
  180.  
  181.     DebugNumToString( num, numStr);
  182.     AppendPString( numStr, msg);
  183.     }
  184.     
  185.         
  186. #if PRAGMA_MARK_SUPPORTED
  187. #pragma mark -
  188. #endif
  189.  
  190.  
  191. /*________________________________________________________________________________
  192.     Build an assert message using several strings.
  193.     
  194.     The awkward mix of C and Pascal strings is due to the fact that the C string
  195.     was generated by the preprocessor whereas all the strings we work with are 
  196.     pascal strings.
  197.  
  198.     if passed "\pNIL ptr:", "theAddress\0", "\pMyRoutine()", construct:
  199.         "\pNIL ptr: 'theAddress' [MyRoutine()]"
  200. ________________________________________________________________________________*/
  201.     static void
  202. BuildBadAddressMsg(
  203.     StringPtr            result,
  204.     ConstStr255Param    prefix,
  205.     const char *        varName,    // a null terminated C string
  206.     ConstStr255Param    suffix)
  207.     {
  208.     result[0]    = 0;
  209.     
  210.     if ( prefix != nil )
  211.         {
  212.         AppendPString( prefix, result);
  213.         }
  214.     
  215.     if ( varName != nil )
  216.         {
  217.         UInt8    temp[256];
  218.         
  219.         CToPString( varName, temp);
  220.         AppendPString( "\p '", result);
  221.         AppendPString( temp, result);
  222.         AppendPString( "\p'", result);
  223.         }
  224.     
  225.     if ( suffix != nil )
  226.         {
  227.         AppendPString( "\p [" /*]*/, result);
  228.         
  229.         AppendPString( suffix, result);
  230.         
  231.         AppendPString( /*[*/ "\p]", result);
  232.         }
  233.     }
  234.  
  235.  
  236.     static void
  237. MemoryDummy(short    dummy)
  238.     {
  239.     ++dummy;
  240.     }
  241.  
  242.  
  243.  
  244. /*________________________________________________________________________________
  245.     Assert (and possibly crash) if the address is invalid
  246.     This routine expressly does not return a value because part of its
  247.     checks may result in a crash if an address is "extra" bogus.
  248. ________________________________________________________________________________*/
  249.     void
  250. _AssertAddressIsValidAlign(
  251.     const void        *addr,
  252.     const char        *varName,
  253.     UInt32            alignCount,
  254.     ConstStr255Param    msg)
  255.     {
  256.     Boolean        aligned    = FALSE;
  257.     Str255        debugMsg;
  258.     
  259.     // a dererenced NIL pointer will result in the value at memory location zero
  260.     Assert( addr != *(const void **)0, "\p_AssertAddressIsValidAlign: addr possibly a dereferenced NIL ptr");
  261.     
  262.     if ( IsNil( msg ) )
  263.         {
  264.         msg    = "\p_AssertAddressIsValidAlign()";
  265.         }
  266.  
  267.     if ( alignCount <= 1 )
  268.         {
  269.         Assert( alignCount > 0, "\p_AssertAddressIsValidAlign: alignment of 0 is illegal");
  270.         aligned    = TRUE;
  271.         }
  272.     else
  273.         {
  274.         aligned    = (((UInt32)addr) % alignCount) == 0;
  275.         }
  276.  
  277.     if ( ! aligned )
  278.         {
  279.         BuildBadAddressMsg( debugMsg, "\pMisaligned address:", varName, msg);
  280.         DebugMsg( debugMsg );
  281.         }
  282.     else if ( ((UInt32)addr) < 256 )
  283.         {
  284.         if ( IsNil( addr ) )
  285.             {
  286.             BuildBadAddressMsg( debugMsg, "\pNIL address:", varName, msg);
  287.             }
  288.         else
  289.             {
  290.             BuildBadAddressMsg( debugMsg, "\pbad address < 256:", varName, msg);
  291.             }
  292.         DebugMsg( debugMsg );
  293.         }
  294.     else
  295.         {
  296.         /*________________________________________________________________________________
  297.         make a memory reference to force a crash here if the handle is invalid
  298.         It is better to force a crash here, 
  299.         rather than crashing in some obscure place in the ROM later
  300.             
  301.         call a dummy routine to hopefully prevent compiler from simply
  302.         optimizing out our dereference.
  303.         ________________________________________________________________________________*/
  304.         MemoryDummy( *(short *)addr );
  305.         }
  306.     
  307.     }
  308.  
  309.  
  310.  
  311. /*________________________________________________________________________________
  312.     Assert (and possibly crash) if the handle is invalid
  313.     This routine expressly does not return a value because part of its
  314.     checks may result in a crash if a handle is "extra" bogus.
  315. ________________________________________________________________________________*/
  316.     void
  317. _AssertHandleIsValid(
  318.     const void *        theHandle,    // void * avoids need to cast
  319.     const char *        varName,    // a null terminated C string
  320.     ConstStr255Param    msg)
  321.     {
  322.     OSErr    err;
  323.     Str255    result;
  324.     
  325.     result[0]    = 0;
  326.     
  327.     if ( IsNil( msg ) )
  328.         {
  329.         msg    = "\pAssertHandleIsValid()";
  330.         }
  331.         
  332.     _AssertAddressIsValidAlign( theHandle, varName, 4, msg);
  333.  
  334.     
  335.     /*________________________________________________________________________________
  336.     make a memory reference to force a crash here if the handle is invalid
  337.     It is better to force a crash here, 
  338.     rather than crashing in some obscure place in the ROM later
  339.     
  340.     call a dummy routine to hopefully prevent compiler from simply
  341.     optimizing out our dereference.
  342.     ________________________________________________________________________________*/
  343.     MemoryDummy( *(short *)theHandle );
  344.         
  345.     (void)HandleZone( (Handle) theHandle );
  346.     err    = MemError();
  347.     if ( IsErr( err ) )
  348.         {
  349.         Str255    errStr;
  350.         
  351.         BuildBadAddressMsg( result, "\perror from HandleZone:", varName, msg);
  352.         
  353.         DebugGetErrorString( err, errStr );
  354.         AppendPString( "\p: ", result);
  355.         AppendPString( errStr, result);
  356.         
  357.         DebugMsg( result );
  358.         }
  359.     
  360.     if ( IsntNil( *(Handle)theHandle )  )
  361.         // check to see if we can get its size (if it has a master pointer)
  362.         {
  363.         (void)GetHandleSize( (Handle) theHandle );
  364.         err    = MemError();
  365.         if ( IsErr( err ) )
  366.             {
  367.             Str255    errStr;
  368.         
  369.             BuildBadAddressMsg( result, "\perror from GetHandleSize:", varName, msg);
  370.             
  371.             DebugGetErrorString( err, errStr );
  372.             AppendPString( "\p: ", result);
  373.             AppendPString( errStr, result);
  374.             
  375.             DebugMsg( result );
  376.             }
  377.         }
  378.     }
  379.  
  380.  
  381.  
  382.     static void
  383. AppendVariableName(
  384.     const char        *varName,    // a null terminated C string
  385.     StringPtr        msg )
  386.     {
  387.     if ( IsntNil( varName ) )
  388.         {
  389.         Str255    tempStr;
  390.  
  391.         // append the variable name in quotes
  392.         CToPString( varName, tempStr);
  393.         AppendPString( "\p '", msg);
  394.         AppendPString( tempStr, msg);
  395.         AppendPString( "\p' ", msg);
  396.         }
  397.     }
  398.  
  399.  
  400.     static void
  401. AppendUserMsg(
  402.     ConstStr255Param    userMsg,
  403.     StringPtr            msg
  404.     )
  405.     {
  406.     if ( IsntNil( userMsg ) )
  407.         {
  408.         AppendPString( "\p <", msg);
  409.         AppendPString( userMsg, msg);
  410.         AppendPString( "\p>", msg);
  411.         }
  412.     }
  413.  
  414.     
  415.  
  416. #if PRAGMA_MARK_SUPPORTED
  417. #pragma mark -
  418. #endif
  419.  
  420.  
  421.  
  422.  
  423. /*————————————————————————————————————————————————————————————————————————————————————————
  424.     A table consists of an array of entries.
  425. ————————————————————————————————————————————————————————————————————————————————————————*/
  426. typedef struct OSErrStringTable
  427.     {
  428.     UInt16                                numEntries;
  429.     const DebugOSErrStringTableEntry    *entries;
  430.     } OSErrStringTable;
  431.  
  432.  
  433. /*————————————————————————————————————————————————————————————————————————————————————————
  434.     Add an error string table to the list of tables. If the table is already
  435.     in our list, do nothing.
  436. ————————————————————————————————————————————————————————————————————————————————————————*/
  437. #define kMaxErrStringTables        20
  438. static OSErrStringTable        sOSErrStringTables[ kMaxErrStringTables ]    = {0,};
  439. static UInt16                sNumOSErrStringTables    = 0;
  440.     void
  441. DebugAddOSErrStringTable(
  442.     const DebugOSErrStringTableEntry    *entries,
  443.     UInt16 numEntries)
  444.     {
  445.     Boolean    alreadyInList    = FALSE;
  446.     UInt16    tableIndex;
  447.  
  448.     // see if we already have the table in our list
  449.     for( tableIndex = 0; tableIndex < sNumOSErrStringTables; ++tableIndex)
  450.         {
  451.         if ( sOSErrStringTables[tableIndex].entries == entries )
  452.             {
  453.             alreadyInList    = TRUE;
  454.             break;
  455.             }
  456.         }
  457.  
  458.     if ( ! alreadyInList )
  459.         {
  460.         if ( sNumOSErrStringTables < kMaxErrStringTables )
  461.             {
  462.             sOSErrStringTables[ sNumOSErrStringTables ].entries        = entries;
  463.             sOSErrStringTables[ sNumOSErrStringTables ].numEntries    = numEntries;
  464.             ++sNumOSErrStringTables;
  465.             }
  466.         else
  467.             {
  468.             DebugMsg("\pDebugAddOSErrStringTable(): no more tables available");
  469.             }
  470.         }
  471.     }
  472.  
  473.  
  474.  
  475. /*————————————————————————————————————————————————————————————————————————————————————————
  476.     search an error table for an error code.
  477.  
  478.     return TRUE if found, FALSE otherwise. If found, return the index in 'foundIndex'
  479. ————————————————————————————————————————————————————————————————————————————————————————*/
  480.     static Boolean
  481. SearchErrorTable(
  482.     OSErr                    err,
  483.     const OSErrStringTable    *table,
  484.     UInt16 *                foundIndex)
  485.     {
  486.     UInt16        pairIndex;
  487.     Boolean        foundIt    = FALSE;
  488.  
  489.     *foundIndex    = 0;
  490.  
  491.     for( pairIndex = 0; pairIndex < table->numEntries; ++pairIndex)
  492.         {
  493.         if ( table->entries[ pairIndex ].err == err )
  494.             {
  495.             foundIt    = TRUE;
  496.             *foundIndex    = pairIndex;
  497.             break;
  498.             }
  499.     }
  500.     return( foundIt );
  501.     }
  502.  
  503.  
  504. // include standard error table
  505. #include "DebugOSErrStrings.inc"
  506.  
  507.     static void
  508. AddDefaultErrorStringTables()
  509.     {
  510.     static Boolean sAddedDefaultTables    = FALSE;
  511.  
  512.     // add the default table to the list if it has not yet been added
  513.     if ( ! sAddedDefaultTables )
  514.         {
  515.         sAddedDefaultTables    = TRUE;
  516.  
  517.         DebugAddOSErrStringTable( sSystemErrorsTable,
  518.             sizeof( sSystemErrorsTable ) / sizeof( sSystemErrorsTable[0] ));
  519.         }
  520.     }
  521.  
  522.  
  523. /*————————————————————————————————————————————————————————————————————————————————————————
  524.     Get a pascal string corresponding to an error code.
  525.     
  526.     If a string was found, return true, otherwise return false and use a numeric code.
  527. ————————————————————————————————————————————————————————————————————————————————————————*/
  528.     static Boolean
  529. DebugGetErrorString(
  530.     OSErr        err,
  531.     StringPtr    errString)
  532.     {
  533.     UInt16        tableIndex;
  534.     Boolean        haveString    = false;
  535.  
  536.     AddDefaultErrorStringTables();
  537.  
  538.     errString[0]    = 0;
  539.     
  540.     // search all error tables one-by-one until we find a match
  541.     for( tableIndex = 0; tableIndex < sNumOSErrStringTables; ++tableIndex)
  542.         {
  543.         const OSErrStringTable    *table;
  544.         UInt16        entryIndex;
  545.  
  546.         table    = &sOSErrStringTables[tableIndex];
  547.  
  548.         if ( SearchErrorTable( err, table, &entryIndex) )
  549.             {
  550.             haveString    = true;
  551.             CToPString( table->entries[entryIndex].cString, errString);
  552.             break;
  553.             }
  554.         }
  555.     
  556.     if ( StrLength( errString ) == 0 )
  557.         {
  558.         // just return the numeric code
  559.         DebugNumToString( err, errString);
  560.         }
  561.     
  562.     return( haveString );
  563.     }
  564.  
  565.  
  566.  
  567. /*————————————————————————————————————————————————————————————————————————————————————————
  568.     Append a string of the following form:
  569.         -34 (dskFulErr) [userMsg]
  570. ————————————————————————————————————————————————————————————————————————————————————————*/
  571.     static void
  572. AppendRemainderOfErrString(
  573.     OSErr                err,
  574.     StringPtr            msgSoFar,
  575.     ConstStr255Param    optionalClientMsg)
  576.     {
  577.     Str255    errString;
  578.     
  579.     // msgSoFor looks like "\pAssertNoErr: "
  580.      
  581.     // append the numeric error code
  582.     DebugNumToString( err, errString);
  583.     AppendPString( errString, msgSoFar);
  584.     AppendPString( "\p ", msgSoFar);
  585.     // msgSoFor now "\pAssertNoErr: -34 "
  586.  
  587.  
  588.     // if available, append the textual (non-numeric) error with parentheses
  589.     if ( DebugGetErrorString( err, errString) )
  590.         {
  591.         AppendPString( "\p (", msgSoFar);
  592.         AppendPString( errString, msgSoFar);
  593.         AppendPString( "\p)", msgSoFar);
  594.         // msgSoFor now "\pAssertNoErr: -34 (dskFulErr)"
  595.         }
  596.  
  597.     // append any additional message
  598.     if ( IsntNil( optionalClientMsg ) )
  599.         {
  600.         AppendPString( "\p [" /*]*/, msgSoFar);
  601.         AppendPString( optionalClientMsg, msgSoFar);
  602.         AppendPString( /*[*/ "\p]", msgSoFar);
  603.         // msgSoFor now "\pAssertNoErr: -34 (dskFulErr) [optionalClientMsg]"
  604.         }
  605.     }
  606.  
  607.  
  608. /*————————————————————————————————————————————————————————————————————————————————————————
  609.     drop into the debugger with an assert if 'err' is not 'noErr'
  610. ————————————————————————————————————————————————————————————————————————————————————————*/
  611.     void
  612. AssertNoErr(
  613.     OSErr                err,
  614.     ConstStr255Param    optionalMsg)
  615.     {
  616. #define IsCancelErr( err )        ( (err) == userCanceledErr )
  617.  
  618.     // don't report cancel errors 
  619.     if ( IsErr( err ) && ! IsCancelErr( err ) )
  620.         {
  621.         Str255    msg;
  622.  
  623.         CopyPString( "\pAssertNoErr: ", msg);
  624.         AppendRemainderOfErrString( err, msg, optionalMsg);
  625.         DebugStr( msg );
  626.         }
  627.     }
  628.  
  629.     
  630.  
  631.  
  632. #endif    // ] DEBUG
  633.